﻿#define _CRT_SECURE_NO_WARNINGS
// ↑ 将本行在代码放在第一行可以避免 scanf的报错问题（仅在VS编译器里）
//【进阶篇】110.指针进阶_函数指针
//【进阶篇】111.指针进阶_函数指针数组：冒泡排序（上）

#include<stdio.h>

#include<string.h>
//某函数 
int  test(const char* str)
{
	return 0;
}
/*案例2 的简化：重命名一个“函数指针【void(*)(int)】”类型名为pf_t */
typedef void(*pf_t)(int);
//计算器菜单
void calcu_menu()
{
	printf("**简易计算器**\n");
	printf("*****1.加*****\n");
	printf("*****2.减*****\n");
	printf("*****3.乘*****\n");
	printf("*****4.除*****\n");
	printf("*****0.退出***\n");
}
//加法
int add(int x,int y)
{
	return (x+y);
}
//减法
int sub(int x, int y)
{
	return (x - y);
}
//乘法
int mul(int x, int y)
{
	return (x * y);
}
//除法
int dvi(int x, int y)
{
	return (x/y);
}
//计算功能
void calc(/*【回调函数】*/int(*pf)(int, int))
{
	int x = 0;
	int y = 0;
	printf("请输入要计算的两个数字:>");
	scanf("%d %d", &x, &y);
	int ret = pf(x,y);
	printf("=%d\n",ret);
}





#include<stdlib.h>
//冒泡升序
void bubble_sort(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz-1; i++) 
	{
		int j = 0;
		int tmp = 0;
		int flag = 1;
		for (j = 0; j < sz-i-1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

//【qsort】|数组起始位置  |数组元素个数   |待排序数据元素大小  |比较函数的函数指针，其中__cdecls是函数调用约定
void qsort(void* base,     size_t number,  size_t width,        int(__cdecl* compare)(const void*, const void*));

//【回调函数】整形升序
int cmp_int(const void* e1, const void* e2)//void*是无具体类型的指针，可以接受任意类型的指针//void*不能解引用，也不能+-整数
{
	return (*(int*)e1 - *(int*)e2);
}	
//【回调函数】整形降序
int cmp_int2(const void* e1, const void* e2)
{

	return (*(int*)e2 - *(int*)e1);
}
//整形冒泡排序_封装为函数
void test1()
{
	int arrmp[] = { 9,8,7,6,5,4,3,2,1 };
	int arrmp1[] = { 8,7,6,5,4,3,2,1,0 };
	int arrmp2[] = { 1,2,3,4,5,6,7,8,9 };
	int sz = sizeof(arrmp) / sizeof(arrmp[0]);
	//升序
	bubble_sort(arrmp, sz);
	qsort(arrmp1, sz, sizeof(arrmp1[0]), cmp_int);//这里就用到了【回调函数】cmp_int
	qsort(arrmp2, sz, sizeof(arrmp2[0]), cmp_int2);//这里就用到了【回调函数】cmp_int2

	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arrmp[i]);
	}
	printf("\n");
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arrmp1[i]);
	}
	printf("\n");
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arrmp2[i]);
	}
	printf("\n");
}


//创建结构体类型
struct Stu
{
	char name[20];
	int age;
};
//【回调函数】按结构体Stu->name排序
int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//【回调函数】按结构体Stu->age排序
int cmp_stu_by_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age-((struct Stu*)e2)->age;
}
//按结构体名称排序_封装为函数
void test2()
{
	struct Stu s[] = { {"zhangsan",15},{"lisi",30},{"wangwu",25} };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}


//交换函数
void swap(char* buf1, char* buf2,int wid)
{
	int i = 0;
	for (i = 0; i < wid; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 =tmp;
		buf1++;
		buf2++;
	}
}
//利用回调函数方式自定义一个冒泡排序函数
void bubble(void* arr, int sz, int wid, int(*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		int tmp = 0;
		int flag = 1;
		for (j = 0; j < sz - i - 1; j++)
		{
					
			if (cmp((char*)arr + j * wid, (char*)arr + (j + 1) * wid) > 0)
			{
				//交换
				swap((char*)arr + j * wid, (char*)arr + (j + 1) * wid,wid);
				flag = 0;
			}
			
		}
		if (flag == 1)
		{
			break;
		}
	}
}
void test3()
{
	int arrmp[] = { 9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arrmp) / sizeof(arrmp[0]);
	//升序
	bubble(arrmp, sz, sizeof(arrmp[0]), cmp_int);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arrmp[i]);
	}
	printf("\n");
}


int main()
{
	//回顾
	{

		//数组
		int a1[2] = { 1,2 }; int a2[2] = { 3,4 }; int a3[2] = { 5,6 }; int a4[2] = { 7,8 };
		char c1= 'a'; char c2 = 'b'; char c3 = 'c'; char c4 = 'd'; char c5 = 'e';
		//数组的指针
		int(*pa1)[2] = &a1;int(*pa2)[2] = &a2;int(*pa3)[2] = &a3;int(*pa4)[2] = &a4;
		char*pc1 = &c1;char*pc2 = &c2;char*pc3= &c3;char*pc4= &c4;char*pc5 = &c5;
		//指针数组//存放指针的数组，元素是指针类型的数组
		int* arr[4] = {pa1,pa2,pa3,pa4};
		char arr2[5] = { *pc1,*pc2,*pc3,*pc4,*pc5};
		int** pp = arr;//二级指针,因为arr本质是个指针，所以可以二级指针指向 arr的地址，即首元素地址=函数名
		//数组指针//存放数组整体地址的指针
		int(*p)[4] = &arr;
		char(*p1)[5] = &arr2;
		char* (*pp1)[5] = &p1;
		//函数指针
		int (*pf2)(const char*) = &test;
		int (*pf)(const char*) = test; //函数名就是函数的地址；
		//所以以上两种表达均可
		(*pf)("abc");  //*pf 解引用 可以指向 test
		(pf)("abc");//pf = &test
		//所以以上两种表达均可
	}
	//函数指针
	{
		//案例 1
		(*(void(*)())0);

		//案例2
		void (*signal(int, void(*)(int)))(int);
		/*用重命名简化*/pf_t signal2(int, pf_t);
	}

	//函数指针用途//实现计算器(整形)利用【回调函数】
	{
		
		
		int input = 0;
		do
		{
			calcu_menu();
			printf("请输入:>");
			scanf("%d", &input);
			switch (input)
			{
				case 1:
				{
					calc(add);
					break;
				}
				case 2:
				{
					calc(sub);
					break;
				}
				case 3:
				{
					calc(mul);
					break;
				}
				case 4:
				{
					calc(dvi);
					break;
				}
				case 0:
				{
					printf("已退出\n");
					break;
				}
				default:
				{
					printf("输入错误\n");
					break;
				}
			}
		} while (input);
	}

	//函数指针数组(利用函数指针数组可以使大型工程代码的增、删、改 更加便捷，同时使得整个函数代码简洁便于阅读维护)
	{
		//add 的函数指针
		int (*p_a)(int, int) = add;
		//加、减、乘、除四个函数的函数指针数组
		/*【转移表】*/int (*pfarr[5])(int, int) = { 0,add, sub, mul, dvi };
		int input = 0;
		do 
		{
			calcu_menu();
			printf("请输入:>");
			scanf("%d", &input);
			if (input == 0)
			{
				printf("已退出\n");
				break;
			}
			else if (input >= 1 && input <= 4)
			{
				int x = 0;
				int y = 0;
				printf("请输入要计算的两个数字:>");
				scanf("%d %d", &x, &y);
				int ret = pfarr[input](x, y);
				printf("=%d\n", ret);
			}
			else
			{
				printf("输入错误\n");
			}
		} while (input);
	}
	//指向函数指针数组的指针
	{
		//函数指针数组
		/*【转移表】*/int (*pfarr[5])(int, int) = { 0,add, sub, mul, dvi };
		int( *(*ppfarr)[5]) = &pfarr;
		//ppfarr与*结合说明它为指针，指针指向的对象的类型是int (* [5])(int, int)
	}

	//回调函数：通过函数指针调用的函数
	{
		//冒泡排序整型
		test1();
		//使用qsort排序结构体
		test2();
		//利用回调函数方式自定义一个冒泡排序函数
		test3();
	}















	return 0;
}
//110.指针进阶_函数指针
	//函数指针可以使用typedef来进行重命名，已方便在使用时进行编辑和表示
//111.指针进阶_函数指针数组：冒泡排序（上）
	//
	//
	//回调函数：通过函数指针调用的函数，
		// 如果把函数的指针作为参数传递给另一个函数，当这个指针被用来调用其所指向的函数时，那么这另一个函数就是回调函数
		// 不是由该函数实现方直接调用，而是在特定事件或条件发生时由另一方调用，用于对该事件或条件进行响应
	// 	
